package com.feilong.modules.service.user.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feilong.modules.common.enums.DeleteEnum;
import com.feilong.modules.common.exception.APIException;
import com.feilong.modules.common.utils.TokenUtil;
import com.feilong.modules.dao.user.UserInfoDao;
import com.feilong.modules.model.user.constant.UserInfoConstant;
import com.feilong.modules.model.user.po.UserInfoPO;
import com.feilong.modules.model.user.request.LoginRequest;
import com.feilong.modules.model.user.vo.UserInfoVO;
import com.feilong.modules.service.user.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoDao, UserInfoPO> implements UserInfoService {

    @Resource
    private RedisTemplate redisTemplate;

    // token过期时间
    public static final Long TOKEN_EXPIRED_TIME = 7200L;

    // redis中私钥的key
    private static final String PRIVATE_KEY = "feilong.privateKey:%s";



    /**
     * 用户登录
     * @param userInfo
     * @return
     */
    @Override
    public UserInfoVO login(LoginRequest userInfo) {
        // 获取私钥
        String privateKey = (String) redisTemplate.opsForValue().get(String.format(PRIVATE_KEY, userInfo.getPublicKey()));
        // 解密
        String account = decrypt(userInfo.getAccount(), privateKey);
        String password = decrypt(userInfo.getPassword(), privateKey);

        // 明文查询数据库账号密码
        List<UserInfoPO> user = this.list(new LambdaQueryWrapper<UserInfoPO>()
                .eq(UserInfoPO::getAccount, account)
                .eq(UserInfoPO::getPassword, password)
                .eq(UserInfoPO::getDeleted, DeleteEnum.NO.getCode())
        );
        // 登录失败
        if (CollectionUtils.isEmpty(user) || user.size() > 1) {
            throw new APIException(UserInfoConstant.USER_LOGIN_FAIL);
        }
        // 登录成功，根据账号密码生成token
        String token = TokenUtil.generateToken(account, password);
        // 存入Redis 有效时间两小时
        redisTemplate.opsForValue().set(String.format(UserInfoConstant.TOKEN_KEY,account), token, TOKEN_EXPIRED_TIME, TimeUnit.SECONDS);
        return UserInfoVO.builder()
                .token(token)
                .build();
    }

    /**
     * 解密
     * @param value
     * @param privateKey
     * @return
     */
    public static String decrypt(String value, String privateKey){
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(value.getBytes(StandardCharsets.UTF_8));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = null;
        //RSA解密
        Cipher cipher = null;
        String result = null;
        try {
            priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            result = new String(cipher.doFinal(inputByte));
        } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
            log.error("{}解密异常，异常：{}", value, e.toString());
        }
        return result;
    }

    /**
     * 加密
     * @param password
     * @return
     */
    public String encryptPassword(String password) {
        // TODO: 2021/4/11 加密密码
        return null;
    }

    /**
     * 用户退出登录
     * @param token
     */
    @Override
    public void loginOut(String token) {
        // jwt校验token，获取用户账号
        String account = TokenUtil.verifyToken(token);
        // redis校验token
        String redisToken = (String) redisTemplate.opsForValue().get(String.format(UserInfoConstant.TOKEN_KEY, account));
        if (StringUtils.isBlank(redisToken)){
            throw new APIException(UserInfoConstant.TOKEN_EXPIRED);
        } else if (!token.equals(redisToken)) {
            throw new APIException(UserInfoConstant.LOGIN_OUT_FAIL);
        }
        // 退出成功，将token的值置为空
        redisTemplate.opsForValue().set(String.format(UserInfoConstant.TOKEN_KEY, account), "");
    }

    /**
     * 根据token获取用户信息
     * @param token
     * @return
     */
    @Override
    public UserInfoVO getUserInfo(String token) {
        // jwt校验token，获取用户账号
        String account = TokenUtil.verifyToken(token);
        // redis校验token
        String redisToken = (String) redisTemplate.opsForValue().get(String.format(UserInfoConstant.TOKEN_KEY, account));
        if (StringUtils.isBlank(redisToken) || (!token.equals(redisToken))){
            throw new APIException(UserInfoConstant.INVALID_TOKEN);
        }
        UserInfoPO userInfoPO = this.getOne(new LambdaQueryWrapper<UserInfoPO>()
                .eq(UserInfoPO::getAccount, account)
                .eq(UserInfoPO::getDeleted, DeleteEnum.NO.getCode())
        );
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(userInfoPO,userInfoVO);
        return userInfoVO;
    }

}
